# include <stdlib.h>   /* malloc */
# include <string.h>   /* memcpy */
# include <stdio.h>    /* printf */

# include "list_forest.h"


/** PSEUDO-RANDO that will generate the same sequence on any machine */ 
int my_rand () {
  static unsigned long next = 1;
  next = next * 1103515245 + 12345;         /*  CTE MAGIC !  */
  return((unsigned)(next/65536) % 32768);   /*  CTE MAGIC !  */
}


/**
   value => int
   element => double
*/


static void test_list_forest_copy_value ( const void * value , void ** pt ) {
  int size = sizeof ( char ) * ( strlen ( value ) + 1 );
  * pt = (void *) malloc ( size ) ;
  memcpy ( * pt , value , size );
}

static void test_list_forest_copy_element ( const void * value , void ** pt ) {
  * pt = malloc ( sizeof ( int ) ) ;
  memcpy ( * pt , value , sizeof ( int ) );
}


static void test_list_forest_free_value ( void ** pt ) {
  free ( * pt ) ;
  * pt = NULL ;
}

static void test_list_forest_free_element ( void ** pt ) {
  free ( * pt ) ;
  * pt = NULL ;
}

static void test_list_forest_fprint_value ( FILE * f ,
					    void * pt ) {
  fprintf ( f , "%s" , (char *) pt ) ;
}

static void test_list_forest_fprint_element ( FILE * f ,
					      void * pt ) {
  fprintf ( f , "%d" , *(int *) pt ) ;
}




# define STRING_MAX 10
static char buffer [ STRING_MAX ] ;

# define NBR_LETTRE 26
# define INT_MAX 30

void random_buffer () {
  int length = 1 + my_rand () % ( STRING_MAX - 1 ) ;
  buffer [ length ] = 0 ;
  while ( length -- ) {
    buffer [ length ] = 'a' + (  my_rand () % NBR_LETTRE ) ;
  }
}


void random_action ( list_forest_position pos ) {  
  int i = 1 + ( my_rand () % INT_MAX );
  random_buffer () ;
  switch ( my_rand () % 30 ) {
  case 0 : 
  case 1 : 
    list_forest_add_left_son ( pos , (void *) buffer ) ;
    break ;
  case 2 : 
  case 3 : 
  case 14 :
    list_forest_add_left_son ( pos , (void *) buffer ) ;
  case 4 : 
  case 7 : 
    if ( list_forest_position_has_left_son  ( pos ) ) {
      list_forest_position_left_son ( pos ) ;
    }
    break ;
  case 5 : 
    list_forest_add_next_brother ( pos , (void *) buffer ) ;
    break ;
  case 6 : 
  case 8 : 
    list_forest_add_next_brother ( pos , (void *) buffer ) ;
  case 9 : 
    if ( list_forest_position_has_next_brother ( pos ) ) {
      list_forest_position_next_brother ( pos ) ;
    }
    break ;
  case 10 :
  case 11 :
  case 12 :
    if ( list_forest_position_has_father ( pos ) ) {
      list_forest_position_father ( pos ) ;
    }
    break ;
  case 13 :
    if ( list_forest_position_has_element ( pos ) ) {
      list_forest_position_element_next ( pos ) ;
    }
    break ;
  default : 
    if ( ! list_forest_position_has_element ( pos ) ) {
      list_forest_add_left_son ( pos , (void *) buffer  ) ;
    }
    list_forest_add_element ( pos , & i ) ;
  }
}



# define REMOVE_SON_MODULO 51
# define REMOVE_BROTHER_MODULO 75

int main ( int argc ,
	   char **  argv ) {
  list_forest lf ;
  if ( argc < 2 ) {
    fprintf ( stderr , "Usage : %s <random move number>\n" , argv [ 0 ] ) ;
    return 1 ;
  }

  /* CREATION */
  list_forest_create ( & lf ,
		       & test_list_forest_copy_value , 
		       & test_list_forest_free_value , 
		       & test_list_forest_copy_element , 
		       & test_list_forest_free_element ) ;
  /* ADDITION */ 
  {
    int i = -1;
    list_forest_position pos1, pos2 ;
    int do_remove_son = FALSE ;
    int do_remove_brother = FALSE ;
    list_forest_position_create ( lf , & pos1 ) ;
    random_buffer () ;
    list_forest_add_next_brother ( pos1 , (void *) buffer ) ;
    list_forest_position_next_brother ( pos1 ) ;
    list_forest_position_create ( lf , & pos2 ) ;

    for ( i = atoi( argv [ 1 ] )  ; i ; i -- ) {
      random_action ( pos1 ) ;
      random_action ( pos2 ) ;
      if ( my_rand () % 30 < 1 ) {
	list_forest_position_destroy ( & pos2 ) ;
	list_forest_position_duplicate ( pos1 , & pos2 ) ;
      }
      if ( 0 == ( i + 1 ) % REMOVE_SON_MODULO ) {
	do_remove_son = TRUE; /* active la coupe du fils */
      }
      if ( do_remove_son && list_forest_position_has_left_son ( pos1 ) ) {
	list_forest_position_left_son_remove ( lf, pos1 ) ;
	list_forest_position_destroy ( & pos2 ) ;
	list_forest_position_duplicate ( pos1 , & pos2 ) ;
	do_remove_son = FALSE ;
      }
      if ( 0 == ( i + 1 ) % REMOVE_BROTHER_MODULO ) {
	do_remove_brother = TRUE; /* active la coupe du fils */
      }
      if ( do_remove_brother && list_forest_position_has_next_brother ( pos1 ) ) {
	list_forest_position_next_brother_remove ( lf, pos1 ) ;
	list_forest_position_destroy ( & pos2 ) ;
	list_forest_position_duplicate ( pos1 , & pos2 ) ;
	do_remove_brother = FALSE ;
      }
    }
    list_forest_position_destroy ( & pos1 ) ;
    list_forest_position_destroy ( & pos2 ) ;
  }

  /* PRINT */
  list_forest_fprint ( stdout , lf , test_list_forest_fprint_value , test_list_forest_fprint_element ) ;

  /* DESTRUCTION */ 
  list_forest_destroy ( & lf ) ; 
 
  return 0 ;
}
